/* 
 * File:   main.cpp
 * Author: fliaali
 *
 * Created on 20 de septiembre de 2013, 14:42
 */

#include <cstdlib>
#include <string.h>
#include <stdio.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <pthread.h>



#include "Utils/Directory.h"
#include "Utils/Parser.h"
#include "Utils/ClusteringKMeans.h"


#define MAX_COMANDO 4
const char* EXTENSION = ".txt";
const char* RUTA_HASHMINS_LEER = "/home/fliaali/Datos/svn/Src/TpDatos/HashMins";
const char* RUTA_HASHMINS = "/home/fliaali/Datos/svn/Src/TpDatos/HashMins/";
const char* PREFIJO_HASHMIN = "/home/fliaali/Datos/svn/Src/TpDatos/HashMins/hashmin_";
const char* RUTA_CLUSTERS = "/home/fliaali/Datos/svn/Src/TpDatos/Clusters";
const char* RUTA_CLUSTER_CREAR = "/home/fliaali/Datos/svn/Src/TpDatos/Clusters/";
const char* PREFIJO_CLUSTER = "/home/fliaali/Datos/svn/Src/TpDatos/Clusters/cluster_";
const char* ARCH_CENTROIDES = "/home/fliaali/Datos/svn/Src/TpDatos/centroides.txt";
const char* ARCH_LISTADO = "/home/fliaali/Datos/svn/Src/TpDatos/listado.txt";
const int MAX_IT_KMEANS = 15;

using namespace std;

typedef enum {
    CLUSTERING, LISTAR_LIBROS, LISTAR_GRUPOS, AGREGAR_LIBRO
} t_opcion;

t_opcion veo_comando_general(int argc, char** argv, char** comandos) {
    int i;
    if (argc == 1) return (t_opcion) - 1;
    for (i = 0; i < MAX_COMANDO; i++) {
        if (strcmp(comandos[i], argv[1]) == 0) return (t_opcion) i;
    }
    return (t_opcion) - 1;
}

int tam = 0;
int cantThreads = 38;
std::vector<std::string> lst;

void *PsrArch(void *threadid) {
    int tid;
    tid = (int) threadid;
    int pos, k, largo, copia;
    Parser* mi_parser = new Parser();
    char nombre[200];
    int desde = (tam * tid);
    int hasta = (tam * (tid + 1));
    if ((tid + 1) == cantThreads)
        hasta = lst.size();
    int j = desde;

    while ((j < lst.size())&&(j < hasta)) {

        const char* archivo = lst[j].c_str();
        //printf("%d %d %s \n", tid, j, archivo);

        for (int k = 0; k < strlen(archivo); k++) {
            if (archivo[k] == '/') pos = k + 1;
        }
        for (int i = 0; i < strlen(nombre); i++) {
            nombre[i] = '\0';
        }

        strcpy(nombre, PREFIJO_HASHMIN);
        largo = strlen(archivo);
        copia = largo - pos;
        strncpy(nombre + strlen(PREFIJO_HASHMIN), archivo + pos, copia);
        nombre[copia + strlen(PREFIJO_HASHMIN)] = '\0';
        mi_parser->Obtener_Vector_HashMin(archivo, nombre);
        j++;
    }

    mi_parser->Destruir();
}

void *Hello(void *threadid) {
    int tid;
    tid = (int) threadid;
    printf("Hello ID %d", tid);
}

void testThreads() {
    pthread_t threads[5];
    int rc;
    int t;
    for (t = 0; t < 5; t++) {
        printf("Creating Thread %d", t);
        rc = pthread_create(&threads[t], NULL, Hello, (void*) t);
        if (rc)
            printf("Error");
    }
    for (t = 0; t < 5; t++) {
        pthread_join(threads[t], NULL);
    }
    pthread_exit(NULL);


}

int Busco_Barra(const char* archivo) {
    int pos = 0;
    for (int k = 0; k < strlen(archivo); k++) {
        if (archivo[k] == '/') pos = k + 1;
    }
    return pos;
}

void Clear(char* s) {
    for (int i = 0; i < strlen(s); i++) {
        s[i] = '\0';
    }
}

void f_clustering(int argc, char** argv) {

    Log* lg = Log::getInstance();
    char* doc_varios_cluster;
    bool varios_cluster = false;
    string ruta_libros;
    int clusters = -1;
    if ((argc != 5) && (argc != 7)) {
        printf("Comando Invalido\n");
        return;
    }

    ruta_libros = argv[2];
    if (argc == 5) {
        doc_varios_cluster = argv[4];
    } else {
        clusters = atoi(argv[4]);
        doc_varios_cluster = argv[6];
    }
    if (strcmp(doc_varios_cluster, "Y") == 0) varios_cluster = true;

    string str = ruta_libros;
    std::vector<std::string> files;
    if (!Directory::Existe(ruta_libros)) {
        printf("No se existe el directorio - %s", str.c_str());
        return;
    }
    Directory::GetListPath(str, files, EXTENSION);
    if (files.empty())
        printf("No se encontraron archivos en el directorio - %s", str.c_str());
    else {
        string stt = RUTA_HASHMINS;
        if (Directory::Existe(stt))
            Directory::Remover(stt);
        else
            Directory::Create(stt);
        int pos, k, largo, copia;
        char nombre[200];
        Clear(nombre);
        Parser* mi_parser = new Parser();
        int archivos_total = files.size();
        Directory::GetListPath(str, lst, EXTENSION);
        printf("Inicia Parseo Archivos %s \n", lg->getDateTimeNow().c_str());

        //

        if (archivos_total > (cantThreads)) {

            tam = archivos_total / cantThreads;

            pthread_t threads[cantThreads];
            int rc;
            int t;
            for (t = 0; t < cantThreads; t++) {
                //printf("Creating Thread %d", t);
                rc = pthread_create(&threads[t], NULL, PsrArch, (void*) t);
                if (rc)
                    printf("Thread - Error");
            }
            for (t = 0; t < cantThreads; t++) {
                pthread_join(threads[t], NULL);
            }
            //
        }
        else {

            for (int i = 0; i < archivos_total; i++) {
                const char* archivo = files[i].c_str();
           //printf("Arch %d %s \n", i, archivo);
           //Generar Archivo Docs (Iddoc=i; Cat(0);Name:files(i)))
                pos = Busco_Barra(archivo);
                Clear(nombre);

                strcpy(nombre, PREFIJO_HASHMIN);
                largo = strlen(archivo);
                copia = largo - pos;
                strncpy(nombre + strlen(PREFIJO_HASHMIN), archivo + pos, copia);
                nombre[copia + strlen(PREFIJO_HASHMIN)] = '\0';
                mi_parser->Obtener_Vector_HashMin(archivo, nombre);
            }
        }


        printf("Fin Parseo Archivos %s \n", lg->getDateTimeNow().c_str());



        if (clusters == -1)
            clusters = ceil(sqrt(archivos_total / 2)); //regla del ORO
        // LLamar a kmeans con los parametros.
        mi_parser->Destruir();

        printf("Inicia Cluster %s \n", lg->getDateTimeNow().c_str());
        vector<string> files;
        string str = RUTA_HASHMINS_LEER;
        Directory::GetListPath(str, files, EXTENSION);
        ClusteringKMeans clustr(files, clusters, files.size(), DEF_NUM_MINHASH, MAX_IT_KMEANS);
        int **cl = clustr.Cluster(varios_cluster);
        clustr.SaveFiles(cl, RUTA_CLUSTER_CREAR, PREFIJO_CLUSTER, ARCH_CENTROIDES, ARCH_LISTADO);
        printf("Fin Cluster %s \n", lg->getDateTimeNow().c_str());


    }
    pthread_exit(NULL);

    return;
}

void f_listar_libros(int argc, char** argv) {
    if (argc != 2) {
        printf("Comando Invalido\n");
        return;
    }
    printf("Listar_Libros \n");
    FILE* listado = fopen(ARCH_LISTADO, "r");
    if (listado == NULL) {
        printf("No existen archivos,realizar clustering");
        return;
    }
    char letra;
    char nombre[200];
    Clear(nombre);
    while (!feof(listado)) {
        letra = fgetc(listado);
        if (letra == '\n') {
            printf("%s \n", nombre);
            Clear(nombre);
        } else {
            nombre[strlen(nombre) + 1] = '\0';
            nombre[strlen(nombre)] = letra;
        }
    }
    fclose(listado);
    return;
}

void f_listar_grupos(int argc, char** argv) {
    if (argc != 2) {
        printf("Comando Invalido\n");
        return;
    }
    printf(" Listar_Grupos\n");
    string str = RUTA_CLUSTERS; // Ruta donde van a estar los clusters alamcenados

    std::vector<std::string> files;
    if (!Directory::Existe(str)) {
        printf("No se encontraron archivos en el directorio");
        return;
    }
    Directory::GetListPath(str, files, EXTENSION);
    if (files.empty()) printf("No se encontraron archivos , relizar clustering");
    else {
        int clusters = files.size();
        FILE* cluster_actual;
        const char* archivo;
        char letra;
        char nombre[200];
        Clear(nombre);
        for (int i = 0; i < clusters; i++) {
            Clear(nombre);
            archivo = files[i].c_str();
            int count =strlen(PREFIJO_CLUSTER);
            printf("\n CLUSTER ");
            while(archivo[count] != '.'){
                printf("%c", archivo[count]);
                count++;
            }
            printf("\n");
            cluster_actual = fopen(archivo, "r");
            while (!feof(cluster_actual)) {
                letra = fgetc(cluster_actual);
                if (letra == '\n') {
                    printf("%s \n", nombre);
                    Clear(nombre);
                } else {
                    nombre[strlen(nombre) + 1] = '\0';
                    nombre[strlen(nombre)] = letra;
                }
            }
            fclose(cluster_actual);
        }
    }
    return;
}

void f_agregar_libro(int argc, char** argv) {
    if (argc != 3) {
        printf("Comando Invalido\n");
        return;
    }
    printf(" Agregar_Libro\n");
    char* ruta_libro = argv[2];
    //Generar Archivo Docs (Iddoc=i; Cat(0);Name:files(i)))
    int pos = 0;
    for (int k = 0; k < strlen(ruta_libro); k++) {
        if (ruta_libro[k] == '/') pos = k + 1;
    }
    char nombre_libro[200];
    char hashmin_nuevo[200];
    strncpy(hashmin_nuevo, PREFIJO_HASHMIN, strlen(PREFIJO_HASHMIN));
    int largo = strlen(ruta_libro);
    strncpy(nombre_libro, ruta_libro + pos, largo - pos);
    nombre_libro[largo - pos] = '\0';
    strncpy(hashmin_nuevo + strlen(PREFIJO_HASHMIN), ruta_libro + pos, largo - pos);
    hashmin_nuevo[strlen(PREFIJO_HASHMIN) + largo - pos] = '\0';
    Parser* mi_parser = new Parser();
    mi_parser->Obtener_Vector_HashMin(ruta_libro, hashmin_nuevo);
    mi_parser->Destruir();
    HashSet* hashset_archivo_nuevo = new HashSet();
    hashset_archivo_nuevo->Load(hashmin_nuevo);
    /*en el archivo donde se guardan los centroides (centroides.txt) necito que se
     guarden las rutas de los hashmin de cada uno de ellos. (es con lo que trabaja kmeans?)
     uno por linea*/
    FILE* centroides = fopen(ARCH_CENTROIDES, "r");
    HashSet* hashset_centroide = new HashSet();
    Jaccard* mi_jaccard = Jaccard::getInstance();
    double jaccard_actual;
    double jaccard_maximo = 0;
    int cluster;
    int i = 0;
    char ruta[200];
    *ruta = 0;
    char letra = 'a';
    while (!feof(centroides)) {
        letra = fgetc(centroides);
        if (letra == '\n') {
            hashset_centroide->Load(ruta);
            jaccard_actual = mi_jaccard->Similarity(hashset_archivo_nuevo, hashset_centroide);
            if (jaccard_actual > jaccard_maximo) {
                jaccard_maximo = jaccard_actual;
                cluster = i;
            }
            i++;
            *ruta = 0;
        } else {
            ruta[strlen(ruta) + 1] = '\0';
            ruta[strlen(ruta)] = letra;
        }
    }
    fclose(centroides);
    hashset_archivo_nuevo->Destroy();
    hashset_centroide->Destroy();
    char ruta_cluster[200];
    strncpy(ruta_cluster, PREFIJO_CLUSTER, strlen(PREFIJO_CLUSTER));
    stringstream stream;
    stream << cluster;
    string scluster = stream.str();
    strncpy(ruta_cluster + strlen(PREFIJO_CLUSTER), scluster.c_str(), strlen(scluster.c_str()));
    ruta_cluster[strlen(PREFIJO_CLUSTER) + strlen(scluster.c_str())] = '\0';
    strncpy(ruta_cluster + strlen(ruta_cluster), ".txt", strlen(".txt"));
    ruta_cluster[strlen(PREFIJO_CLUSTER) + strlen(scluster.c_str()) + 4] = '\0';
    FILE* archivo_cluster = fopen(ruta_cluster, "a");
    FILE* listado = fopen(ARCH_LISTADO, "a");
    fprintf((FILE*) archivo_cluster, "%s\n", nombre_libro);
    fprintf((FILE*) listado, "%s   %s\n", nombre_libro, scluster.c_str());
    fclose(archivo_cluster);
    fclose(listado);
    return;
}

void testLog() {
    Log* lg = Log::getInstance();
    lg->ERROR("Prueba Error");
}

void testHashMin() {
    HashMin* hm = new HashMin();
    int e = hm->Execute();
    hm->Save("/home/fliaali/Descargas/Test/HashMin.txt");
}

void testDirectory() {
    string str = "/home/fliaali/Descargas/Test";
    string ext = ".h";
    std::vector<std::string> files;
    Directory::GetListPath(str, files, ext);
    bool e1 = Directory::Existe(str);
    bool r1 = Directory::Existe(ext);
    Directory::Remover(str);

    string stt = "/home/fliaali/Descargas/Test/Test1/";
    Directory::Create(stt);
}

void testCluster() {
    int **tabla, i, row = 20, col = 2;
    tabla = new int*[row];
    for (i = 0; i < row; i++)
        tabla[i] = new int[col];
    tabla[0][0] = 65;
    tabla[0][1] = 220;
    tabla[1][0] = 73;
    tabla[1][1] = 160;
    tabla[2][0] = 59;
    tabla[2][1] = 110;

    tabla[3][0] = 61;
    tabla[3][1] = 120;
    tabla[4][0] = 75;
    tabla[4][1] = 150;
    tabla[5][0] = 67;
    tabla[5][1] = 240;

    tabla[6][0] = 68;
    tabla[6][1] = 230;
    tabla[7][0] = 70;
    tabla[7][1] = 220;
    tabla[8][0] = 62;
    tabla[8][1] = 130;
    tabla[9][0] = 66;
    tabla[9][1] = 210;

    tabla[10][0] = 77;
    tabla[10][1] = 190;
    tabla[11][0] = 75;
    tabla[11][1] = 180;
    tabla[12][0] = 74;
    tabla[12][1] = 170;

    tabla[13][0] = 70;
    tabla[13][1] = 210;
    tabla[14][0] = 61;
    tabla[14][1] = 110;
    tabla[15][0] = 58;
    tabla[15][1] = 100;

    tabla[16][0] = 66;
    tabla[16][1] = 230;
    tabla[17][0] = 59;
    tabla[17][1] = 120;
    tabla[18][0] = 68;
    tabla[18][1] = 210;
    tabla[19][0] = 61;
    tabla[19][1] = 130;

    vector<string> files;
    string str = RUTA_HASHMINS_LEER;
    Directory::GetListPath(str, files, EXTENSION);
    ClusteringKMeans clustr(files, 3, files.size(), DEF_NUM_MINHASH, 30);
    int **cl = clustr.Cluster(false);
    clustr.SaveFiles(cl, RUTA_CLUSTER_CREAR, PREFIJO_CLUSTER, ARCH_CENTROIDES, ARCH_LISTADO);

    for (int k = 0; k < 3; ++k) // cada cluster
    {
        cout << "CLUSTER" << k << " ";

        cout << "\n";
        for (int i = 0; i < files.size(); ++i) {// cada tupla
            if (cl[i][0] == k) {
                cout << files[i] << " ";
                cout << "\n";
                //for (int j = 0; j < col; ++j)
                //  cout << tabla[i][j] <<  " ";
                //cout << "\n";
            }
        }
        cout << "\n";
    }
}

void Prueba_Parser() {
    Parser* mi_parser = new Parser();
    mi_parser->Obtener_Vector_HashMin("Arch.txt", "pepe.txt");
    int i = 0;
    FILE* veo_chars = fopen("pepe.txt", "r");
    while (feof(veo_chars) == 0) {
        char letra = fgetc(veo_chars);
        i++;
    }
    printf("%d", i);
    fclose(veo_chars);
    mi_parser->Destruir();
}

void Prueba_Hash_Shingle() {
    Hashs* mi_hash = new Hashs();
    char* hash = mi_hash->F_Hashing_Shingle("onking");
    char* result = (char*) malloc(sizeof (char)*4);
    int c1 = 115;
    int c2 = 84;
    int c3 = 134;
    int c4 = 152;
    strcat(result, (char*) &c1);
    strcat(result, (char*) &c2);
    strcat(result, (char*) &c3);
    strcat(result, (char*) &c4);

    if (strcmp(result, hash) == 0) printf("Oh Yeah!");
}

void TestFClustering() {
    string str = "/home/fliaali/Descargas/Test";
    std::vector<std::string> files;
    Directory::GetListPath(str, files, EXTENSION);
    if (files.empty()) printf("No se encontraron archivos en el directorio");
    else {
        string stt = "/home/fliaali/Repositorio Datos/svn/Src/TpDatos/HashMins/";
        if (Directory::Existe(stt)) Directory::Remover(stt);
        Directory::Create(stt);
        int pos, k, largo;
        char* nombre;
        Parser* mi_parser = new Parser();
        int archivos_total = files.size();
        for (int i = 0; i < archivos_total; i++) {
            const char* archivo = files[i].c_str();
            //Generar Archivo Docs (Iddoc=i; Cat(0);Name:files(i)))
            pos = 0;
            for (k = 0; k < strlen(archivo); k++) {
                if (archivo[k] == '/') pos = k + 1;
            }
            *nombre = 0;
            strcpy(nombre, "HashMins/hashmin_");
            largo = strlen(archivo);
            strncpy(nombre + 17, archivo + pos, largo - pos);
            mi_parser->Obtener_Vector_HashMin(archivo, nombre);
        }
        mi_parser->Destruir();
    }
    return;
}

void ToString(int i, int largo, char* salida) {
    int ind = largo - 1;
    while (i > 0) {
        salida[ind] = (char) (i % 10 + 48);
        i /= 10;
        ind--;
    }
    while (ind >= 0) {
        salida[ind] = '0';
        ind--;
    }
}

void TestConvert() {
    int i = 55;
    char str[MAX_COMANDO];
    printf("%d", strlen(str));
    ToString(i, 4, str);
    printf("%s", str);
}

void testHashSet() {
    //HashSet 0
    HashSet* hs0 = new HashSet();
    hs0->Load("/home/fliaali/Repositorio Datos/svn/Src/TpDatos/HashMins/hashmin_aerosmith-helter skelter.txt");
    int a = hs0->GetElem(0);
    int b = hs0->GetElem(1);

    //HashSet 1
    HashSet* hs1 = new HashSet();
    hs1->Load(hs0->GetVector());
    int a1 = hs1->GetElem(0);
    int b1 = hs1->GetElem(1);

    //HashSet 2
    HashSet* hs2 = new HashSet();
    hs2->Load("/home/fliaali/Repositorio Datos/svn/Src/TpDatos/HashMins/hashmin_daft punk - give life back to music.txt");

    //Jaccard
    Jaccard* j = Jaccard::getInstance();
    double similitud1 = j->Similarity(hs0, hs1); // 1
    double similitud2 = j->Similarity(hs0, hs2);

    hs0->Destroy();
    hs1->Destroy();
    hs2->Destroy();

    int s = 5;
}

int main(int argc, char** argv) {

    //testHashSet();
    //testHashMin(); 
    //Prueba_Parser();
    //testCluster();
    //Prueba_Hash_Shingle();
    //TestFClustering();
    //TestConvert();
    //testDirectory();
    //f_listar_grupos(2,argv);
    //char** a;
    //f_agregar_libro(3,a);
    //testThreads();

    char* comandos[] = {"-d", "-l", "-g", "-a"};
    t_opcion opcion;
    opcion = veo_comando_general(argc, argv, comandos);
    switch (opcion) {
        case CLUSTERING:
        {
            f_clustering(argc, argv);
            break;
        }
        case LISTAR_LIBROS:
        {
            f_listar_libros(argc, argv);
            break;
        }
        case LISTAR_GRUPOS:
        {
            f_listar_grupos(argc, argv);
            break;
        }
        case AGREGAR_LIBRO:
        {
            f_agregar_libro(argc, argv);
            break;
        }
        default: printf("Comando Invalido\n");

    }

    return 0;
}


